/*
 * ChartPanel.cpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   ChartPanel.cpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   2 nov 2012
 */

#include <wx/wx.h>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <wx/statline.h>

#include "TltPlotWindow.hpp"
#include "ChartZoomPanel.hpp"

#include "ChartPanel.hpp"

using boost::shared_ptr;

namespace tltgen {

  BEGIN_EVENT_TABLE(ChartPanel, wxPanel)
  EVT_COMMAND(ID_ChartZoomPanel, wxEVT_VERTICAL_START_CHANGED, ChartPanel::onVerticalStartChanged)
  EVT_COMMAND(ID_ChartZoomPanel, wxEVT_VERTICAL_END_CHANGED, ChartPanel::onVerticalEndChanged)
  EVT_COMMAND(ID_ChartZoomPanel, wxEVT_HORIZONTAL_START_CHANGED, ChartPanel::onHorizontalStartChanged)
  EVT_COMMAND(ID_ChartZoomPanel, wxEVT_HORIZONTAL_END_CHANGED, ChartPanel::onHorizontalEndChanged)
  EVT_TLT_PLOT_MOUSE(ID_PlotWindow, ChartPanel::onPlotWindowMouseMotion)
  END_EVENT_TABLE()


  ChartPanel::ChartPanel(wxWindow* parent, wxWindowID id, const shared_ptr<Plot>& plot, const wxPoint& pos, const wxSize& size) :
    wxPanel(parent, id, pos, size),
    plot(plot)
  {
    wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);

    wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);

    this->plotWindow = new TltPlotWindow(this, ID_PlotWindow, wxDefaultPosition, wxDefaultSize, 0, wxPLPLOT_BACKEND_GC);
    hbox->Add(this->plotWindow, 1, wxEXPAND);

    wxStaticLine *line = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL);
    hbox->Add(line, 0, wxEXPAND);

    wxPanel *propertiesPanel = createPropertiesPanel(this);
    hbox->Add(propertiesPanel, 0, wxEXPAND | wxALL, 2);

    plot->connectDataChangedObserver(boost::bind(&ChartPanel::onPlotDataChanged, this));
    plot->connectNbDatasetsChangedObserver(boost::bind(&ChartPanel::onPlotNbDatasetsChanged, this, _1, _2));
    drawPlot();

    vbox->Add(hbox, 1, wxEXPAND);

    this->statusBar = new wxStatusBar(this, wxID_ANY, 0);
    vbox->Add(this->statusBar);

    SetSizer(vbox);
  }

  ChartPanel::~ChartPanel()
  { }

  wxPanel *ChartPanel::createPropertiesPanel(wxWindow* parent)
  {
    wxPanel *panel = new wxPanel(parent);
    wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
    panel->SetSizer(vbox);

    this->checkBoxPanel = createCheckBoxPanel(panel);
    for (unsigned int i = 0; i < this->plot->getNbDatasets(); ++i) {
      addCheckBox(i);
    }
    vbox->Add(checkBoxPanel, 0, wxEXPAND | wxALL, 3);

    vbox->AddStretchSpacer();

    this->chartZoomPanel = new ChartZoomPanel(panel, ID_ChartZoomPanel);
    this->chartZoomPanel->setVerticalRange(this->plot->getYBounds().get<0>(), plot->getYBounds().get<1>());
    this->chartZoomPanel->setHorizontalRange(this->plot->getXBounds().get<0>(), plot->getXBounds().get<1>());
    vbox->Add(chartZoomPanel, 0, wxEXPAND | wxALL, 3);

    return panel;
  }

  wxPanel *ChartPanel::createCheckBoxPanel(wxWindow* parent)
  {
    wxPanel* panel = new wxPanel(parent);
    wxSizer* vbox = new wxBoxSizer(wxVERTICAL);
    panel->SetSizer(vbox);

    wxStaticText *stDescription = new wxStaticText(panel, wxID_ANY, _("Visible plots"));
    vbox->Add(stDescription, 0, 0, 0);

    return panel;
  }

  void ChartPanel::addCheckBox(unsigned int i)
  {
    wxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);

    wxCheckBox* checkBox = new wxCheckBox(this->checkBoxPanel, wxID_ANY, wxString::FromUTF8(this->plot->getDatasetName(i).c_str()));
    checkBox->SetValue(true);
    Connect(checkBox->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ChartPanel::onGraphSelectionChange));
    hbox->Add(checkBox, 0, wxLEFT, 2);
    this->checkBoxes.push_back(checkBox);

    wxStaticText* checkBoxLabel = new wxStaticText(this->checkBoxPanel, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(70, wxDefaultSize.GetHeight()));
    hbox->Add(checkBoxLabel, 0, wxLEFT, 7);
    this->checkBoxLabels.push_back(checkBoxLabel);

    this->checkBoxPanel->GetSizer()->Add(hbox);
  }

  void ChartPanel::removeCheckBox(unsigned int i)
  {
    wxCheckBox* checkBox = this->checkBoxes.at(i);
    this->checkBoxPanel->GetSizer()->Remove(checkBox->GetId());
    checkBox->Destroy();

    this->checkBoxes.erase(this->checkBoxes.begin()+i);
  }

  void ChartPanel::onGraphSelectionChange(wxCommandEvent& WXUNUSED(event))
  {
    drawPlot();
  }

  void ChartPanel::onPlotWindowMouseMotion(TltPlotMouseEvent& event)
  {
    wxString message;
    message.Printf(wxT("X: %.2f  Y: %.2f"), event.getX(), event.getY());
    this->statusBar->SetStatusText(message);
    
    for (unsigned int i = 0; i < this->plot->getNbDatasets(); ++i) {
      PLFLT y = this->plot->getYValue(i, event.getX());
      wxString checkBoxLabel;
      if (! std::isnan(y)) {
	checkBoxLabel.Printf(wxT("%.2f"), i, y);
      }
      this->checkBoxLabels.at(i)->SetLabel(checkBoxLabel);
    }
  }  


  void ChartPanel::onPlotDataChanged()
  {
    drawPlot();
    
    //TODO: implement separate event?
    if (this->chartZoomPanel->isValidRange(this->plot->getYBounds().get<0>(), plot->getYBounds().get<1>())) {
      this->chartZoomPanel->setVerticalRange(this->plot->getYBounds().get<0>(), plot->getYBounds().get<1>());
    } else {
      this->chartZoomPanel->setVerticalRange(0, 1);
    }
    if (this->chartZoomPanel->isValidRange(this->plot->getXBounds().get<0>(), plot->getXBounds().get<1>())) {
      this->chartZoomPanel->setHorizontalRange(this->plot->getXBounds().get<0>(), plot->getXBounds().get<1>());
    } else {
      this->chartZoomPanel->setHorizontalRange(0, 1);
    }
  }


  void ChartPanel::onPlotNbDatasetsChanged(Plot::NbDatasetsChangedEvent e, unsigned int index)
  {
    switch (e) {
    case Plot::DATASET_ADDED:
      addCheckBox(index);
      break;
    case Plot::DATASET_REMOVED:
      removeCheckBox(index);
      break;
    }
    drawPlot();
  }

  void ChartPanel::onVerticalStartChanged(wxCommandEvent& event)
  {
    this->plot->setYBounds(event.GetInt(), this->plot->getYBounds().get<1>());
  }
  void ChartPanel::onVerticalEndChanged(wxCommandEvent& event)
  {
    this->plot->setYBounds(this->plot->getYBounds().get<0>(), event.GetInt());
  }
  void ChartPanel::onHorizontalStartChanged(wxCommandEvent& event)
  {
    this->plot->setXBounds(event.GetInt(), this->plot->getXBounds().get<1>());
  }
  void ChartPanel::onHorizontalEndChanged(wxCommandEvent& event)
  {
    this->plot->setXBounds(this->plot->getXBounds().get<0>(), event.GetInt());
  }

  void ChartPanel::drawPlot()
  {
    wxPLplotstream *pls = getPlotWindow()->GetStream();
    pls->clear();

    this->plot->plotViewport(pls);
    for (unsigned int i = 0; i < this->plot->getNbDatasets(); ++i) {
      if (getCheckBox(i)->IsChecked()) {
        this->plot->plotDataset(pls, i);
      }
    }
    getPlotWindow()->RenewPlot();
  }

  wxPLplotwindow *ChartPanel::getPlotWindow() const
  {
    return this->plotWindow;
  }
  /*Plot *ChartPanel::getPlot() const
  {
    return this->plot.get();
  }*/
  wxCheckBox *ChartPanel::getCheckBox(unsigned int i) const
  {
    if (i < 0 || i >= this->plot->getNbDatasets()) {
      throw std::invalid_argument("Index '" + boost::lexical_cast<std::string>(i) + 
				  "' is not a valid checkbox index.");
    }
    return this->checkBoxes.at(i);
  }

}


